from __future__ import absolute_import, division

from gui.core import *

from direct.gui.DirectGui import *
from panda3d.core import *


class PandaDialog(Object):
    def on_click(self):
        pass

    def assign_primitive(self, object, primitive, *args, **kwds):
        kwds.update({'resources': self.primitive.resources,
                     'scene'    : self.primitive.scene,
                     'mouse'    : self.primitive.mouse})
        
        object.render_with(primitive, *args, **kwds)


class PandaUIElement(GUIElementPrimitive):
    resources = Attribute()
    scene     = Attribute()
    mouse     = Attribute()

    visible = Attribute(initiable = False, initial = True, writable = True)
    
    children = SetAttribute(initiable = False)    
    
    def setup(self):
        self.texture = self.resources.load_gui_texture(self.texture_name)
        self.object  = DirectFrame(parent = self.scene, frameTexture = self.texture)
        self.object.setTransparency(TransparencyAttrib.MAlpha)
        
        self.setup_element()
    
    def setup_element(self):
        pass
    
    def add_child(self, child):
        self._children.add(child)
        
        child.create_in(self)
        
        return child
    
    @property
    def size(self):
        return (self.screen.width_from_pixels(self.texture.getXSize()),
                self.screen.height_from_pixels(self.texture.getYSize()))
    
    @property
    def raw_size(self):
        return (self.width, -self.height)
    
    @property
    def panda_position(self):
        return (self.model.left, 0, self.model.top)
    
    @property
    def panda_size(self):
        return (0, self.model.raw_width, self.model.raw_height, 0)
    
    @property
    def panda_area(self):
        return Position(self.model.left, self.model.top).extend_by(self.model.raw_width,
                                                                   self.model.raw_height,
                                                                   continuous = True)
    
    def update(self):
        self.on_update()
        
        for child in self.children:
            child.update()
        
        self.post_update()
    
    def on_update(self):
        pass
    
    def post_update(self):
        pass
    
    def on_move(self):
        if not self.screen.is_initialized:
            return
        
        self.object['frameSize'] = self.panda_size
        self.object.setPos(*self.panda_position)
        
        for child in self.children:
            child.update_motion_dependent()
    
    def on_resize(self):
        self.object['frameTexture'] = self.texture
    
    def show(self):
        self.visible = True
        
        self.object.show()
    
    def hide(self):
        self.visible = False
        
        self.object.hide()


class PandaNodePath(Object):
    id        = Attribute()
    node_path = Attribute(initiable = False)
    parent    = Attribute(initiable = False)
    scene     = parent._.scene
    resources = parent._.resources
    screen    = parent._.screen
    mouse     = parent._.mouse
    
    position = Attribute(initial = (0, 0), writable = True)
    visible  = Attribute(initial = True, writable = True, changed = lambda self: self.update_visibility())
    
    is_initialized = Attribute(initial = False, initiable = False, writable = True)    
    mouse_was_up   = Attribute(initial = False, initiable = False, writable = True)
    mouse_was_down = Attribute(initial = False, initiable = False, writable = True)
    
    @property
    def relative_mouse(self):
        if not self.visible or not self.mouse.hasMouse():
            return None
        
        pointer  = self.mouse.getMouse()
        relative = self.node_path.getRelativePoint(self.scene, (pointer.getX(), 0, pointer.getY()))
        
        return (self.screen.raw_pixels_from_width(relative.getX()),
                self.screen.raw_pixels_from_height(relative.getZ()))
        
    @property
    def has_mouse(self):
        if not self.is_initialized or self.area is None or not self.visible or not self.mouse.hasMouse():
            return False
        
        dx, dy = self.relative_mouse
        
        return (self.left <= self.x + dx <= self.right and
                self.top  <= self.y + dy <= self.bottom)
    
    @property
    def is_mouse_down(self):
        return (self.mouse.isButtonDown(MouseButton.one()) == 1)
    
    @property
    def is_mouse_up(self):
        return (self.mouse.isButtonDown(MouseButton.one()) != 1)
    
    @property
    def has_mouse_down(self):
        return self.has_mouse and self.is_mouse_down
    
    @property
    def has_mouse_up(self):
        return self.has_mouse and self.is_mouse_up

    @property
    def object(self):
        return self.node_path
    
    @property
    def x(self):
        return self.position[0]
    
    @property
    def y(self):
        return self.position[1]
    
    @property
    def area(self):
        return None
    
    @property
    def left(self):
        return self.area[0]
    
    @property
    def right(self):
        return self.area[1]
    
    @property
    def top(self):
        return self.area[2]
    
    @property
    def bottom(self):
        return self.area[3]
    
    @property
    def node(self):
        if self.node_path is None:
            return None
        else:
            return self.node_path.node()
    
    @property
    def new_node(self):
        """Called to create the underlying Panda3D representation. Returning the ID will make a
        new empty node path. Subclasses can return a PandaNode instead.
        """
        return self.id
    
    def create_in(self, element):
        self.is_initialized = False
        self.parent         = element
        self.node_path      = element.object.attachNewNode(self.new_node)
        self.node_path.hide()   # Hide to prevent artifacts from showing while the
                                # screen is still setting up
        
        self.setup_new_node()
    
    def setup_new_node(self):
        pass
    
    def update(self):
        if not self.node_path.isHidden():
            if self.mouse_was_up and self.has_mouse_down:
                self.on_mouse_down()
                
                self.mouse_was_down = True
            
            if self.mouse_was_down and self.is_mouse_up:
                self.on_mouse_up()
                
                if self.has_mouse:
                    self.on_clicked()
                
                self.mouse_was_down = False
        
        self.on_update()
        
        if not self.node_path.isHidden():
            self.mouse_was_up = self.has_mouse_up
    
    def on_mouse_down(self):
        pass
    
    def on_mouse_up(self):
        pass
    
    def on_clicked(self):
        pass
        
    def on_update(self):
        pass
    
    def show(self):
        self.visible = True
    
    def hide(self):
        self.visible = False
    
    def update_motion_dependent(self):
        self.is_initialized = True
        
        self.update_position()
        self.update_visibility()
    
    def update_visibility(self):
        if self.node_path is None or not self.is_initialized:
            return
        
        if self.visible:
            self.node_path.show()
        else:
            self.node_path.hide()
    
    def update_position(self):
        self.node_path.setPos(self.screen.raw_width_from_pixels(self.x), 0,
                              self.screen.raw_height_from_pixels(self.y))


class PandaEvent(Object):
    id =_ (None)
    
    def handle(self, *args, **kwds):
        pass


__all__ = [name for name in dir() if not name.startswith('_') or name == '_']
